/*
 * @Author: owenhe owenhe@msn.cn
 * @Date: 2025-05-09 14:58:51
 * @LastEditors: owenhe owenhe@msn.cn
 * @LastEditTime: 2025-05-19 11:07:10
 * @FilePath: /xhs-collector/entrypoints/api/coze-api.ts
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */

import { browser } from 'wxt/browser';
import { storage } from '#imports'; // For loading API token and Bot ID from storage
import {
  CozeAPI,
  RoleType,     // Enum from @coze/api (seems to work)
  ChatStatus,   // Enum from @coze/api (seems to work)
  COZE_CN_BASE_URL, // Const from @coze/api (seems to work)
  // Removed attempts to import specific Message/Payload/Completion types
} from '@coze/api';
import { FEISHU_SETTINGS_KEY } from '../popup/App';

// --- Constants for Storage ---

const COZE_BASE_URL_STORAGE_KEY = 'local:cozeBaseUrl'; // Optional: if user can override base URL
const COZE_BOT_ID = '7502263371510038580';
// --- Custom Types based on usage/example ---
// Type for messages sent to the SDK
export interface SDKMessageInput {
  role: RoleType;
  content: string;
  content_type: 'text';
}

// Type for messages received in the SDK's response
interface SDKMessageOutput {
  role: RoleType;
  type: string;
  content: string;
  content_type: string;
}

// Type for the chat object within the SDK's response
interface SDKChatObject {
  chat_id?: string;
  bot_id: string;
  status: ChatStatus;
  conversation_id: string;
  error?: { code?: number; message?: string; err_msg?: string };
  usage?: any;
}

// Type for the overall response from createAndPoll
interface SDKChatResponse {
  chat: SDKChatObject;
  messages?: SDKMessageOutput[];
}

// --- Types (simplified as SDK handles most) ---
// We can use CozeSDKMessage directly or create a simpler internal type if needed.
export interface ChatMessage {
  role: 'user' | 'assistant'; // Simplified roles for internal use
  content: string;
}

export interface CozeChatResult {
  success: boolean;
  messages?: ChatMessage[];
  error?: string;
  conversationId?: string;
  usage?: any; // Usage type might be more specific in ChatCompletion.usage
}

// --- Workflow Types ---
// 使用 SDK 的实际类型
type WorkflowRunResult = {
  execute_id: string;
  // 其他字段根据实际 SDK 返回类型动态处理
  [key: string]: any;
}

type WorkflowHistoryItem = {
  execute_id: string;
  // 其他字段根据实际 SDK 返回类型动态处理
  [key: string]: any;
}

// --- API Parameter Types ---
interface ChatParams {
  query: string;
  botId?: string;
  chatHistory?: ChatMessage[];
}

interface RunWorkflowStreamParams {
  workflowId: string;
  parameters?: Record<string, any>;
  botId?: string;
}

interface ChatWorkflowStreamParams {
  workflowId: string;
  messages: SDKMessageInput[];
  parameters?: Record<string, any>;
  botId?: string;
}

// --- API Client Class using @coze/api SDK ---
class CozeApiClient {
  private client: CozeAPI | null = null;
  private defaultBotId: string | null = null;
  private isInitialized = false;

  constructor() {
    // Initialization will be async and called before first API use
    // or explicitly by the consuming code.
  }

  // Async initialization to load settings from storage
  async initialize(): Promise<void> {
    if (this.isInitialized) return;
    console.log('初始化Coze API Client', await storage.getItem(FEISHU_SETTINGS_KEY));
    const storedToken = 'pat_etrT1H3f2M6t93n6wDh0OcXXaPk2QnxWlPLUvU7qEplkX8gwpN0Xp6rJ5xCW1iyZ';
    this.defaultBotId = COZE_BOT_ID;
    const storedBaseUrl = await storage.getItem<string>(COZE_BASE_URL_STORAGE_KEY);

    if (!storedToken) {
      console.warn(
        'Coze API Token not found in storage. Please configure it via extension settings.'
      );
      // Optionally, you could prevent client initialization or throw an error
      // For now, client will be null and API calls will fail gracefully.
      this.isInitialized = true;
      return;
    }

    this.client = new CozeAPI({
      token: storedToken,
      // Default to COZE_CN_BASE_URL if not specified by user, or COZE_COM_BASE_URL if international
      baseURL: COZE_CN_BASE_URL, // Use CN_BASE_URL from SDK or user preference
      allowPersonalAccessTokenInBrowser: true,
    });

    if (!this.defaultBotId) {
      console.warn(
        'Default Coze Bot ID not found in storage. Please configure it.'
      );
    }

    this.isInitialized = true;
    console.log('Coze API Client Initialized.');
  }

  private async ensureInitialized(): Promise<void> {
    if (!this.isInitialized) {
      await this.initialize();
    }
  }

  public async chat({
    query,
    botId,
    chatHistory = []
  }: ChatParams): Promise<CozeChatResult> {
    await this.ensureInitialized();

    if (!this.client) {
      return {
        success: false,
        error: 'Coze API client not initialized (Missing API Token?)',
      };
    }

    const currentBotId = botId || this.defaultBotId;
    if (!currentBotId) {
      return {
        success: false,
        error: 'Bot ID not configured. Please provide one or set a default.',
      };
    }

    // Use MessagePayload for creating messages to send
    const messagesToSdk: SDKMessageInput[] = [];
    for (const msg of chatHistory) {
      messagesToSdk.push({
        role: msg.role === 'user' ? RoleType.User : RoleType.Assistant,
        content: msg.content,
        content_type: 'text',
      });
    }
    messagesToSdk.push({
      role: RoleType.User,
      content: query,
      content_type: 'text',
    });

    try {
      // The createAndPoll response type is ChatCompletion from the SDK
      const response = await this.client.chat.createAndPoll({
        bot_id: currentBotId,
        additional_messages: messagesToSdk,
        // user_id: userId, // Add if SDK supports/requires it here and you want per-call user ID
      }) as SDKChatResponse; // Cast to our defined response type
      console.log('Coze API Response:', response);
  
      if (response.chat.status === ChatStatus.COMPLETED) {
        const assistantMessages: ChatMessage[] = (response.messages || [])
          .filter((msg: SDKMessageOutput) =>
            msg.role === RoleType.Assistant &&
            msg.type === 'answer' &&
            msg.content_type === 'text'
          )
          .map((msg: SDKMessageOutput) => ({
            role: 'assistant',
            content: msg.content,
          }));
        return {
          success: true,
          messages: assistantMessages,
          conversationId: response.chat.conversation_id,
          usage: response.chat.usage,
        };
      } else if (response.chat.status === ChatStatus.FAILED) {
        console.error('Coze API Chat Failed:', response.chat.error);
        // Access error information, checking if response.chat.error exists
        const errorMsg = response.chat.error?.message || response.chat.error?.err_msg || 'Unknown error';
        return {
          success: false,
          error: `Chat failed: ${errorMsg}`,
          conversationId: response.chat.conversation_id,
        };
      } else {
        // Handle other statuses like IN_PROGRESS, or if polling times out without completion/failure
        console.warn('Coze API Chat status:', response.chat.status);
        return {
          success: false,
          error: `Chat ended with status: ${response.chat.status}`,
          conversationId: response.chat.conversation_id,
        };
      }
    } catch (error) {
      console.error('Error calling Coze API via SDK:', error);
      return {
        success: false,
        error: error instanceof Error ? error.message : 'Unknown SDK error',
      };
    }
  }

  public async runWorkflowStream({
    workflowId,
    parameters = {},
    botId
  }: RunWorkflowStreamParams): Promise<AsyncIterable<any>> {
    await this.ensureInitialized();

    if (!this.client) {
      throw new Error('Coze API client not initialized (Missing API Token?)');
    }

    const currentBotId = botId || this.defaultBotId;
    if (!currentBotId) {
      throw new Error('Bot ID not configured. Please provide one or set a default.');
    }

    if (!workflowId) {
      throw new Error('Workflow ID is required');
    }

    return this.client.workflows.runs.stream({
      workflow_id: workflowId,
      parameters,
      bot_id: currentBotId,
    });
  }

  public async runWorkflow({
    workflowId,
    parameters = {},
    botId,
    isAsync = false
  }: {
    workflowId: string;
    parameters?: Record<string, any>;
    botId?: string;
    isAsync?: boolean;
  }): Promise<WorkflowRunResult> {
    await this.ensureInitialized();

    if (!this.client) {
      throw new Error('Coze API client not initialized (Missing API Token?)');
    }

    const currentBotId = botId || this.defaultBotId;
    if (!currentBotId) {
      throw new Error('Bot ID not configured. Please provide one or set a default.');
    }

    if (!workflowId) {
      throw new Error('Workflow ID is required');
    }

    const result = await this.client.workflows.runs.create({
      workflow_id: workflowId,
      parameters,
      bot_id: currentBotId,
      is_async: isAsync,
    });

    return result as WorkflowRunResult;
  }

  public async chatWorkflowStream({
    workflowId,
    messages,
    parameters = {},
    botId
  }: ChatWorkflowStreamParams): Promise<AsyncIterable<any>> {
    await this.ensureInitialized();

    if (!this.client) {
      throw new Error('Coze API client not initialized (Missing API Token?)');
    }

    const currentBotId = botId || this.defaultBotId;
    if (!currentBotId) {
      throw new Error('Bot ID not configured. Please provide one or set a default.');
    }

    if (!workflowId) {
      throw new Error('Workflow ID is required');
    }

    return this.client.workflows.chat.stream({
      workflow_id: workflowId,
      parameters,
      bot_id: currentBotId,
      additional_messages: messages,
    });
  }

  public async getWorkflowHistory(
    workflowId: string,
    executeId: string
  ): Promise<WorkflowHistoryItem[]> {
    await this.ensureInitialized();

    if (!this.client) {
      throw new Error('Coze API client not initialized (Missing API Token?)');
    }

    if (!workflowId || !executeId) {
      throw new Error('Workflow ID and Execute ID are required');
    }

    const history = await this.client.workflows.runs.history(workflowId, executeId);
    return history as WorkflowHistoryItem[];
  }
}

// Export an instance of the client
export const cozeApiClient = new CozeApiClient();

// Example of how to initialize and use (e.g., in a background script or on popup load):
/*
async function initializeAndTestCoze() {
  await cozeApiClient.initialize();
  
  const userQuery = "你好，今天天气怎么样？";

  console.log('Sending chat request to Coze...');
  const result = await cozeApiClient.chat({
    query: userQuery,
    chatHistory: []
  });

  if (result.success && result.messages) {
    console.log("Coze Response Messages:");
    result.messages.forEach(msg => console.log(`- ${msg.role}: ${msg.content}`));
    console.log("Conversation ID:", result.conversationId);
    console.log("Token Usage:", result.usage);
  } else {
    console.error("Coze Chat Error:", result.error);
  }
}

async function testWorkflow() {
  await cozeApiClient.initialize();
  
  // 1. Stream workflow example
  try {
    const stream = await cozeApiClient.runWorkflowStream({
      workflowId: 'your-workflow-id',
      parameters: { input: 'test' }
    });
    for await (const event of stream) {
      console.log('Stream event:', event);
    }
  } catch (error) {
    console.error('Stream workflow error:', error);
  }

  // 2. Non-stream workflow example
  try {
    const result = await cozeApiClient.runWorkflow({
      workflowId: 'your-workflow-id',
      parameters: { input: 'test' }
    });
    console.log('Workflow result:', result);
  } catch (error) {
    console.error('Non-stream workflow error:', error);
  }

  // 3. Chat workflow example
  try {
    const messages = [{
      role: RoleType.User,
      content: 'Hello workflow',
      content_type: 'text'
    }];
    const chatStream = await cozeApiClient.chatWorkflowStream({
      workflowId: 'your-workflow-id',
      messages,
      parameters: {}
    });
    for await (const event of chatStream) {
      console.log('Chat event:', event);
    }
  } catch (error) {
    console.error('Chat workflow error:', error);
  }

  // 4. Async workflow with history polling example
  try {
    const asyncResult = await cozeApiClient.runWorkflow({
      workflowId: 'your-workflow-id',
      parameters: { input: 'test' },
      isAsync: true
    });
    console.log('Async workflow started:', asyncResult);

    while (true) {
      await new Promise(resolve => setTimeout(resolve, 1000));
      const history = await cozeApiClient.getWorkflowHistory('your-workflow-id', asyncResult.execute_id);
      console.log('Workflow history:', history);
      if (history[0]?.['status'] !== 'Running' && history[0]?.['execute_status'] !== 'Running') {
        break;
      }
    }
  } catch (error) {
    console.error('Async workflow error:', error);
  }
}
*/
